/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.client;

import java.util.Arrays;


import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;

import fr.openparts.OpenParts.logging.client.OPLogger;
import fr.openparts.OpenParts.logging.client.OPLogging;

/**
 * The OPBindingResource class is a binding object representing a binded resource, containing {@link OPBindingAction}. OPBindingResource is a JavaScriptObject,
 * that is a real javascript object mapped to Java thanks to GWT overlay absolutely fantastic technique.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 31 December 2009
 */
public final class OPBindingResource extends JavaScriptObject {

    protected static final OPLogger logger = OPLogging.getLoggerForDomain("binding");

    /**
     * Create and return a new OPBindingResource object for the given resource name.
     * 
     * @param resourceName
     *            the name of the resource to bind. Shouldn't be null.
     * @return the corresponding OPBindingResource. Cannot be null.
     */
    public final static OPBindingResource resourceFactory(final String resourceName) {
        assert resourceName != null : "parameter resourceName shouldn't be null";

        return resourceFactoryImpl(resourceName);
    };

    /**
     * Real implementation of creating and returning a new OPBindingResource object for the given resource name.
     * 
     * @param resourceName
     *            the name of the resource to bind.
     * @return the corresponding OPBindingResource.
     */
    private final static native OPBindingResource resourceFactoryImpl(final String resourceName) /*-{
        var resource = {};
        resource.name = resourceName;
        resource.actions = {};
        return resource;
    }-*/;

    /**
     * Protected constructor, necessary because it's a JavaScriptObject.
     */
    protected OPBindingResource() {}

    /**
     * Return the name of the resource represented by this binding object.
     * 
     * @return the name of the resource represented by this binding object. Cannot be null.
     */
    public final native String getResourceName() /*-{
        return this.name;
    }-*/;

    /**
     * Returns an array of names of all actions binded for this service.
     * 
     * @return an array of names of all actions binded for this service.
     */
    public final String[] getActionsNames() {
        final JsArrayString actionsNames = getJsActionsNames();
        String[] result = new String[actionsNames.length()];
        for (int i = 0; i < actionsNames.length(); i++) {
            result[i] = actionsNames.get(i);
        }
        return result;
    }

    /**
     * Returns a native js array containing all actions names.
     * 
     * @return a native js array containing all actions names. Can be empty, cannot be null.
     */
    private final native JsArrayString getJsActionsNames() /*-{
        var names = new Array();
        var count = 0;

        for ( var p in this.actions) {
            if (this.actions.hasOwnProperty(p)) {
                names[count] = p;
                ++count;
            }
        }

        return names;
    }-*/;

    /**
     * Return the {@link OPBindingAction} object corresponding to the given action binded in this resource. Return null if there is no such action binded.
     * 
     * @param actionName
     *            the name of the searched binded action. Shouldn't be null.
     * @return the {@link OPBindingAction} corresponding to this action, or null if there is no such action binded.
     */
    public final OPBindingAction getAction(final String actionName) {
        assert actionName != null : "parameter actionName shouldn't be null";

        return getActionImpl(actionName);
    }

    /**
     * Real implementation of returning the {@link OPBindingAction} object corresponding to the given action binded in this resource. Return null if there is no
     * such action binded.
     * 
     * @param actionName
     *            the name of the searched binded action.
     * @return the {@link OPBindingAction} corresponding to this action, or null if there is no such action binded.
     */
    private final native OPBindingAction getActionImpl(final String actionName) /*-{
        if (!this.actions[actionName]) {
            return null;
        }

        return this.actions[actionName];
    }-*/;

    /**
     * Bind the given {@link OPBindingAction} object as action for this resource. If an action with the same name was already binded, just change its value.
     * 
     * @param action
     *            the action to bind. Shouldn't be null.
     */
    public final void bindAction(final OPBindingAction action) {
        assert action != null : "parameter action shouldn't be null";
        logger.log("Binding action " + action.getActionName()  + " for resource "  + getResourceName()
                + (action.getMandatoryParamsNames().length == 0 ? " with no mandatory params" : " with mandatory params " + Arrays.toString(action.getMandatoryParamsNames())));

        bindActionImpl(action);
    }

    /**
     * Real implementation for binding the given {@link OPBindingAction} object as action for this resource.
     * 
     * @param action
     *            the action to bind.
     */
    private final native void bindActionImpl(final OPBindingAction action) /*-{
        this.actions[action.name] = action;
    }-*/;

}
